Build a Flutter Food Delivery App
Building a Flutter Food Delivery App is a practical project that combines Flutter UI development, navigation, state management, reusable widgets, authentication, cloud database integration, cart functionality, order management, and responsive application design. In this project, we will create a food delivery application where users can browse restaurants and food items, view product details, add food to a cart, manage quantities, place orders, and view their order history.
This project is designed to demonstrate how individual Flutter concepts work together to create a complete real-world application.
1. Project Objectives
The main objective of this project is to build a practical food delivery application using Flutter and Dart.
- Create a modern food delivery user interface.
- Create reusable Flutter widgets.
- Implement bottom navigation.
- Display restaurants and food categories.
- Display food items dynamically.
- Create food detail screens.
- Implement add-to-cart functionality.
- Manage cart item quantities.
- Calculate subtotal, delivery fee, and total amount.
- Implement user authentication.
- Store food and restaurant information in Firestore.
- Store customer orders in Firestore.
- Display order history.
- Create a complete food ordering workflow.
- Prepare the application for production deployment.
2. What We Will Build
The application will contain multiple screens connected through navigation.
| Screen | Purpose |
| Splash Screen | Displays application branding while the application starts. |
| Login Screen | Allows existing users to sign in. |
| Registration Screen | Allows new users to create an account. |
| Home Screen | Displays categories, restaurants, and recommended food. |
| Restaurant Screen | Displays food items belonging to a restaurant. |
| Food Detail Screen | Displays detailed information about a food item. |
| Cart Screen | Displays selected food items and pricing. |
| Checkout Screen | Allows users to confirm delivery and payment information. |
| Order Success Screen | Confirms that the order has been placed. |
| Orders Screen | Displays previous and current orders. |
| Profile Screen | Displays user information and account options. |
3. Technologies Used
| Technology | Purpose |
| Flutter | Builds the cross-platform application. |
| Dart | Programming language used by Flutter. |
| Firebase Authentication | Handles user registration and login. |
| Cloud Firestore | Stores restaurants, food items, users, and orders. |
| Firebase Storage | Can be used for food and restaurant images. |
| Provider / Riverpod / Bloc | Can be used for application state management. |
| Material Design | Provides Flutter UI components and design patterns. |
4. Prerequisites
Before starting this project, you should understand basic Flutter and Dart concepts.
- Dart variables and data types.
- Functions.
- Classes and objects.
- Constructors.
- Inheritance and polymorphism.
- Lists and maps.
- Flutter widgets.
- StatelessWidget and StatefulWidget.
- Layouts using Row, Column, Stack, and Container.
- ListView and GridView.
- Navigation.
- Basic asynchronous programming.
- Basic Firebase concepts.
5. Create the Flutter Project
Create a new Flutter project using the Flutter CLI.
flutter create food_delivery_app
Move into the project directory:
cd food_delivery_app
Run the project:
flutter run
6. Recommended Project Structure
A proper project structure makes a large Flutter application easier to maintain.
lib/
├── main.dart
├── app.dart
├── models/
│ ├── food_model.dart
│ ├── restaurant_model.dart
│ ├── cart_item_model.dart
│ └── order_model.dart
├── screens/
│ ├── splash_screen.dart
│ ├── login_screen.dart
│ ├── register_screen.dart
│ ├── home_screen.dart
│ ├── restaurant_screen.dart
│ ├── food_detail_screen.dart
│ ├── cart_screen.dart
│ ├── checkout_screen.dart
│ ├── orders_screen.dart
│ └── profile_screen.dart
├── widgets/
│ ├── food_card.dart
│ ├── restaurant_card.dart
│ ├── category_item.dart
│ ├── cart_item.dart
│ └── custom_button.dart
├── services/
│ ├── auth_service.dart
│ ├── firestore_service.dart
│ └── order_service.dart
├── providers/
│ ├── cart_provider.dart
│ └── user_provider.dart
└── utils/
├── app_colors.dart
├── app_constants.dart
└── validators.dart
7. Application Flow
The overall application flow can be represented as follows:
Application Start
|
v
Splash Screen
|
v
Check Authentication
/ \
Logged In Logged Out
| |
v v
Home Screen Login Screen
| |
| v
| Registration
| |
+--------------+
|
v
Home Screen
|
v
Select Restaurant
|
v
Select Food
|
v
Food Details
|
v
Add To Cart
|
v
Cart
|
v
Checkout
|
v
Place Order
|
v
Order Confirmation
|
v
Order History
8. Create the Main Entry Point
The main.dart file is the entry point of the Flutter application.
import 'package:flutter/material.dart';
import 'app.dart';
void main() {
runApp(const FoodDeliveryApp());
}
9. Create the Main Application
The main application widget defines the overall application configuration.
import 'package:flutter/material.dart';
import 'screens/splash_screen.dart';
class FoodDeliveryApp extends StatelessWidget {
const FoodDeliveryApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Food Delivery App',
theme: ThemeData(
useMaterial3: true,
),
home: const SplashScreen(),
);
}
}
10. Create Application Constants
Application constants help avoid repeating the same values throughout the project.
class AppConstants {
static const String appName = 'Food Delivery';
static const double deliveryFee = 40.0;
static const double taxRate = 0.05;
}
11. Create Food Model
A model represents the structure of food data.
class Food {
final String id;
final String name;
final String description;
final double price;
final String imageUrl;
final String category;
final String restaurantId;
final bool isAvailable;
Food({
required this.id,
required this.name,
required this.description,
required this.price,
required this.imageUrl,
required this.category,
required this.restaurantId,
required this.isAvailable,
});
}
12. Create Restaurant Model
class Restaurant {
final String id;
final String name;
final String imageUrl;
final String description;
final double rating;
final String deliveryTime;
Restaurant({
required this.id,
required this.name,
required this.imageUrl,
required this.description,
required this.rating,
required this.deliveryTime,
});
}
13. Create Cart Item Model
A cart item contains food information and the quantity selected by the customer.
class CartItem {
final Food food;
int quantity;
CartItem({
required this.food,
this.quantity = 1,
});
double get totalPrice => food.price * quantity;
}
14. Create Order Model
class OrderModel {
final String id;
final String userId;
final List<CartItem> items;
final double totalAmount;
final String address;
final String status;
final DateTime createdAt;
OrderModel({
required this.id,
required this.userId,
required this.items,
required this.totalAmount,
required this.address,
required this.status,
required this.createdAt,
});
}
15. Create the Splash Screen
The splash screen introduces the application before navigating to the appropriate screen.
import 'package:flutter/material.dart';
import 'home_screen.dart';
import 'login_screen.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 2), () {
if (!mounted) return;
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => const LoginScreen(),
),
);
});
}
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text(
'Food Delivery',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
),
);
}
}
16. Design the Login Screen
The login screen allows existing customers to access their accounts.
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
TextField(
controller: emailController,
decoration: const InputDecoration(
labelText: 'Email',
),
),
const SizedBox(height: 16),
TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {},
child: const Text('Login'),
),
],
),
),
);
}
}
17. Registration Screen
The registration screen collects the customer's basic account information.
- Name
- Email
- Password
- Phone number
- Delivery address
18. Home Screen
The home screen is the primary discovery screen of the application. It should provide quick access to categories, restaurants, popular foods, and search functionality.
A typical home screen can contain:
- Location selector.
- Search field.
- Food categories.
- Promotional banner.
- Popular restaurants.
- Recommended food.
- Bottom navigation.
19. Search Bar
The search bar allows customers to find restaurants or food items quickly.
TextField(
decoration: InputDecoration(
hintText: 'Search food or restaurants',
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
)
20. Food Categories
Food categories make discovery easier.
| Category | Example Foods |
| Pizza | Margherita, Farmhouse, Paneer Pizza |
| Burger | Veg Burger, Cheese Burger |
| Indian | Paneer, Biryani, Thali |
| Chinese | Noodles, Fried Rice, Manchurian |
| Desserts | Cake, Ice Cream, Brownie |
| Drinks | Juice, Coffee, Shake |
21. Category Widget
class CategoryItem extends StatelessWidget {
final String title;
final IconData icon;
const CategoryItem({
super.key,
required this.title,
required this.icon,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
CircleAvatar(
radius: 28,
child: Icon(icon),
),
const SizedBox(height: 6),
Text(title),
],
);
}
}
22. Restaurant Card
A reusable restaurant card displays important restaurant information.
class RestaurantCard extends StatelessWidget {
final Restaurant restaurant;
const RestaurantCard({
super.key,
required this.restaurant,
});
@override
Widget build(BuildContext context) {
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
restaurant.imageUrl,
height: 160,
width: double.infinity,
fit: BoxFit.cover,
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
restaurant.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text('Rating: ${restaurant.rating}'),
Text(restaurant.deliveryTime),
],
),
),
],
),
);
}
}
23. Food Card
The food card is one of the most frequently reused widgets in the application.
class FoodCard extends StatelessWidget {
final Food food;
final VoidCallback onTap;
const FoodCard({
super.key,
required this.food,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
food.imageUrl,
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
food.name,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
),
),
Text(food.description),
const SizedBox(height: 6),
Text(
'₹${food.price.toStringAsFixed(2)}',
),
],
),
),
],
),
),
),
);
}
}
24. Restaurant Details Screen
When the user selects a restaurant, the application should display restaurant information and available food items.
- Restaurant image.
- Restaurant name.
- Rating.
- Delivery time.
- Restaurant description.
- Food categories.
- Food list.
25. Food Detail Screen
The food detail screen displays complete information about a selected food item.
class FoodDetailScreen extends StatelessWidget {
final Food food;
const FoodDetailScreen({
super.key,
required this.food,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(food.name),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
food.imageUrl,
height: 260,
width: double.infinity,
fit: BoxFit.cover,
),
Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
food.name,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(food.description),
const SizedBox(height: 16),
Text(
'₹${food.price.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
),
);
}
}
26. Add to Cart
The Add to Cart operation is a core feature of the food delivery application.
When a customer taps the Add to Cart button, the selected food item should be added to the cart. If the same food item already exists in the cart, its quantity should normally increase rather than creating an unnecessary duplicate item.
27. Cart State Management
The cart is shared across multiple screens, so a state management solution is useful.
Possible approaches include:
- Provider
- Riverpod
- Bloc
- ChangeNotifier
- ValueNotifier for smaller applications
28. Simple Cart Provider
import 'package:flutter/foundation.dart';
class CartProvider extends ChangeNotifier {
final List<CartItem> _items = [];
List<CartItem> get items => _items;
void addItem(Food food) {
final index = _items.indexWhere(
(item) => item.food.id == food.id,
);
if (index >= 0) {
_items[index].quantity++;
} else {
_items.add(CartItem(food: food));
}
notifyListeners();
}
void removeItem(Food food) {
_items.removeWhere(
(item) => item.food.id == food.id,
);
notifyListeners();
}
void increaseQuantity(Food food) {
final item = _items.firstWhere(
(item) => item.food.id == food.id,
);
item.quantity++;
notifyListeners();
}
void decreaseQuantity(Food food) {
final item = _items.firstWhere(
(item) => item.food.id == food.id,
);
if (item.quantity > 1) {
item.quantity--;
} else {
_items.remove(item);
}
notifyListeners();
}
double get subtotal {
return _items.fold(
0,
(sum, item) => sum + item.totalPrice,
);
}
void clearCart() {
_items.clear();
notifyListeners();
}
}
29. Cart Screen
The cart screen should clearly show every selected item.
| Information | Example |
| Food Name | Paneer Pizza |
| Unit Price | ₹299 |
| Quantity | 2 |
| Item Total | ₹598 |
30. Quantity Controls
Each cart item should provide controls to increase or decrease the quantity.
Row(
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.remove),
),
const Text('1'),
IconButton(
onPressed: () {},
icon: const Icon(Icons.add),
),
],
)
31. Price Calculation
The checkout amount can be calculated using the following formula:
Subtotal = Sum of all item totals
Tax = Subtotal × Tax Rate
Total = Subtotal + Tax + Delivery Fee
Example:
| Item | Amount |
| Food Items | ₹700 |
| Delivery Fee | ₹40 |
| Tax | ₹35 |
| Total | ₹775 |
32. Checkout Screen
The checkout screen is responsible for collecting final order information before the order is submitted.
- Delivery address.
- Contact number.
- Order summary.
- Subtotal.
- Delivery fee.
- Tax.
- Total amount.
- Payment method.
- Place Order button.
33. Address Management
A real-world food delivery application should allow customers to manage delivery addresses.
- Home address.
- Work address.
- Other address.
- Default address.
34. Payment Method UI
For a learning project, payment methods can initially be represented as UI options.
RadioListTile<String>(
value: 'Cash',
groupValue: selectedPayment,
onChanged: (value) {
setState(() {
selectedPayment = value!;
});
},
title: const Text('Cash on Delivery'),
)
For real online payments, a secure payment gateway should be integrated through its official Flutter SDK or backend-supported payment flow.
35. Place Order
When the customer taps Place Order, the application should validate all required information before creating the order.
if (cartItems.isEmpty) {
return;
}
if (addressController.text.trim().isEmpty) {
return;
}
// Create order
// Save order to Firestore
// Clear cart
// Navigate to success screen
36. Order Success Screen
After successful order creation, display a confirmation message.
Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.check_circle,
size: 80,
),
const SizedBox(height: 20),
const Text(
'Order Placed Successfully!',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {},
child: const Text('View Orders'),
),
],
),
),
)
37. Order Status
Each order can have a status representing its current stage.
| Status | Meaning |
| Pending | Order has been submitted and is awaiting confirmation. |
| Confirmed | Restaurant has accepted the order. |
| Preparing | Food is being prepared. |
| Ready | Food is ready for pickup or delivery. |
| Out for Delivery | Delivery partner is taking the order to the customer. |
| Delivered | Order has been delivered. |
| Cancelled | Order has been cancelled. |
38. Order Tracking Flow
Order Placed
|
v
Pending
|
v
Confirmed
|
v
Preparing
|
v
Ready
|
v
Out for Delivery
|
v
Delivered
39. Bottom Navigation
Bottom navigation provides quick access to the main sections of the application.
| Tab | Purpose |
| Home | Browse food and restaurants. |
| Search | Search for food and restaurants. |
| Cart | Manage selected items. |
| Orders | View current and previous orders. |
| Profile | Manage account settings. |
40. Bottom Navigation Example
BottomNavigationBar(
currentIndex: currentIndex,
onTap: (index) {
setState(() {
currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
label: 'Cart',
),
BottomNavigationBarItem(
icon: Icon(Icons.receipt_long),
label: 'Orders',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
)
41. Navigation Between Screens
Flutter provides Navigator APIs for moving between screens.
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const CartScreen(),
),
);
To return to the previous screen:
Navigator.pop(context);
42. Named Routes
For larger applications, named routes can make navigation easier to organize.
MaterialApp(
routes: {
'/': (context) => const SplashScreen(),
'/login': (context) => const LoginScreen(),
'/home': (context) => const HomeScreen(),
'/cart': (context) => const CartScreen(),
'/orders': (context) => const OrdersScreen(),
},
)
43. Firebase Integration
Firebase can provide backend services without requiring developers to build a complete custom backend from scratch.
For this project, Firebase can be used for:
- Authentication.
- Cloud Firestore.
- Firebase Storage.
- Analytics.
- Cloud Messaging.
44. Configure Firebase
Create a Firebase project and connect the Flutter application to it using the official Firebase Flutter configuration process.
The Firebase configuration should be kept separate from application UI code.
45. Firebase Authentication
Firebase Authentication can be used to manage customer accounts.
Typical authentication flow:
User Registration
|
v
Firebase Authentication
|
v
User Account Created
|
v
User Login
|
v
Authenticated User
|
v
Food Delivery Home Screen
46. Authentication Service
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<UserCredential> register(
String email,
String password,
) {
return _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
}
Future<UserCredential> login(
String email,
String password,
) {
return _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
}
Future<void> logout() {
return _auth.signOut();
}
}
47. User Profile Data
Authentication manages account credentials, while additional profile information can be stored in Firestore.
Example profile data:
{
"name": "Customer Name",
"email": "[email protected]",
"phone": "9999999999",
"address": "Customer Address",
"createdAt": "timestamp"
}
48. Firestore Database Structure
A possible Firestore structure for this application is:
users/
userId/
name
email
phone
address
restaurants/
restaurantId/
name
description
imageUrl
rating
deliveryTime
foods/
foodId/
name
description
price
imageUrl
category
restaurantId
isAvailable
orders/
orderId/
userId
items
totalAmount
address
paymentMethod
status
createdAt
49. Add Food to Firestore
await FirebaseFirestore.instance
.collection('foods')
.add({
'name': 'Paneer Pizza',
'description': 'Delicious paneer pizza',
'price': 299,
'category': 'Pizza',
'restaurantId': 'restaurant_001',
'imageUrl': 'IMAGE_URL',
'isAvailable': true,
});
50. Read Food Data from Firestore
final snapshot = await FirebaseFirestore.instance
.collection('foods')
.get();
for (final document in snapshot.docs) {
print(document.data());
}
51. Display Firestore Data with StreamBuilder
StreamBuilder is useful when the UI should automatically respond to real-time Firestore changes.
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('foods')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (!snapshot.hasData) {
return const Center(
child: Text('No food available'),
);
}
final foods = snapshot.data!.docs;
return ListView.builder(
itemCount: foods.length,
itemBuilder: (context, index) {
final food = foods[index];
return ListTile(
title: Text(food['name']),
subtitle: Text(
'₹${food['price']}',
),
);
},
);
},
)
52. Store an Order in Firestore
await FirebaseFirestore.instance
.collection('orders')
.add({
'userId': currentUserId,
'items': cartItems.map((item) {
return {
'foodId': item.food.id,
'name': item.food.name,
'price': item.food.price,
'quantity': item.quantity,
};
}).toList(),
'totalAmount': totalAmount,
'address': address,
'paymentMethod': paymentMethod,
'status': 'Pending',
'createdAt': FieldValue.serverTimestamp(),
});
53. Read User Orders
final orders = FirebaseFirestore.instance
.collection('orders')
.where(
'userId',
isEqualTo: currentUserId,
)
.snapshots();
54. Order History Screen
The order history screen should show:
- Order ID.
- Order date.
- Food items.
- Total amount.
- Order status.
- Delivery address.
55. Search and Filtering
Food delivery applications contain large amounts of food and restaurant data. Search and filtering improve the customer experience.
Useful filters include:
- Food category.
- Price range.
- Restaurant rating.
- Vegetarian or non-vegetarian.
- Delivery time.
- Popular items.
56. Local Search Filtering
final filteredFoods = foods.where((food) {
return food.name
.toLowerCase()
.contains(searchText.toLowerCase());
}).toList();
57. Favorites Feature
Customers can save frequently ordered foods or restaurants as favorites.
Set<String> favoriteFoodIds = {};
void toggleFavorite(String foodId) {
if (favoriteFoodIds.contains(foodId)) {
favoriteFoodIds.remove(foodId);
} else {
favoriteFoodIds.add(foodId);
}
}
58. Ratings and Reviews
A production-level application can allow customers to rate restaurants and food items.
| Field | Example |
| userId | user_123 |
| restaurantId | restaurant_001 |
| rating | 5 |
| review | Great food and fast delivery. |
| createdAt | Firestore Timestamp |
59. Delivery Address Validation
Before placing an order, validate that the delivery address is available and complete.
if (addressController.text.trim().length < 10) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please enter a valid address'),
),
);
return;
}
60. Form Validation
Forms should validate user input before submitting data.
- Email should have a valid format.
- Password should meet minimum requirements.
- Phone number should contain valid digits.
- Address should not be empty.
- Required checkout fields should be completed.
61. Loading States
Network operations may take time. Always provide visual feedback while data is loading.
if (isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
62. Error Handling
Applications should handle Firebase and network errors gracefully.
try {
await authService.login(
email,
password,
);
} on FirebaseAuthException catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
e.message ?? 'Login failed',
),
),
);
}
63. Empty States
Every list should consider the possibility that no data is available.
if (items.isEmpty) {
return const Center(
child: Text(
'No items available',
),
);
}
64. Responsive Design
The application should adapt to different screen sizes.
Flutter provides several useful tools for responsive layouts:
- MediaQuery.
- LayoutBuilder.
- Expanded.
- Flexible.
- FractionallySizedBox.
- OrientationBuilder.
65. Responsive Food Grid
final width = MediaQuery.of(context).size.width;
int columns;
if (width > 1200) {
columns = 4;
} else if (width > 700) {
columns = 3;
} else {
columns = 2;
}
GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
childAspectRatio: 0.8,
),
itemBuilder: (context, index) {
return FoodCard(
food: foods[index],
onTap: () {},
);
},
)
66. Network Images
Food delivery applications commonly display remote images for restaurants and food items.
Image.network(
food.imageUrl,
fit: BoxFit.cover,
errorBuilder: (
context,
error,
stackTrace,
) {
return const Icon(
Icons.image_not_supported,
);
},
)
67. Image Caching
For production applications with many remote images, image caching can improve scrolling performance and reduce repeated downloads.
68. Food Delivery App UI Components
| Component | Flutter Widget Examples |
| Search | TextField |
| Category | Container, CircleAvatar |
| Restaurant | Card, ListView |
| Food Grid | GridView |
| Cart | ListView, Card |
| Checkout | Form, TextFormField |
| Payment Selection | RadioListTile |
| Navigation | NavigationBar / BottomNavigationBar |
| Dialogs | showDialog |
| Notifications | SnackBar |
69. Recommended Color Structure
A food delivery application generally benefits from a consistent visual system.
class AppColors {
static const primary = Color(0xFFFF6B35);
static const background = Color(0xFFF8F8F8);
static const textPrimary = Color(0xFF222222);
static const textSecondary = Color(0xFF777777);
static const success = Color(0xFF2E7D32);
}
70. Theme Configuration
ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.deepOrange,
scaffoldBackgroundColor: Colors.white,
appBarTheme: const AppBarTheme(
centerTitle: true,
),
)
71. Admin Side Features
A complete food delivery platform may also contain an admin interface.
- Add restaurants.
- Edit restaurants.
- Delete restaurants.
- Add food items.
- Edit food items.
- Disable unavailable food.
- View customer orders.
- Update order status.
- Manage categories.
- View basic sales information.
72. Admin Order Workflow
Customer Places Order
|
v
Order Saved to Firestore
|
v
Admin / Restaurant Dashboard
|
v
Confirm Order
|
v
Update Status
|
v
Customer Receives Updated Status
|
v
Order Delivered
73. Firebase Security Rules
Firestore security rules are important because client applications should not have unrestricted access to database data.
A conceptual rule structure may restrict users to their own order records and allow authorized administrative operations separately.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write:
if request.auth != null
&& request.auth.uid == userId;
}
match /orders/{orderId} {
allow read, create:
if request.auth != null;
allow update:
if request.auth != null;
}
}
}
Production security rules should be carefully designed according to the application's actual authorization model.
74. Real-Time Order Status
Firestore streams can allow the customer interface to react when an order status changes.
FirebaseFirestore.instance
.collection('orders')
.doc(orderId)
.snapshots()
For example:
Restaurant updates:
Pending → Preparing
Customer screen:
Automatically displays:
"Your food is being prepared."
75. Push Notifications
Firebase Cloud Messaging can be considered for notifications such as:
- Order confirmed.
- Food preparation started.
- Order ready.
- Order out for delivery.
- Order delivered.
- Promotional notifications.
76. Cart Validation Before Checkout
Before placing an order, verify that:
- The cart is not empty.
- All food items are still available.
- Prices are current.
- Delivery address is valid.
- The user is authenticated.
- The final amount is calculated again.
77. Preventing Duplicate Orders
Order creation should be designed carefully so that repeated taps or network retries do not unintentionally create duplicate orders.
A production application can use an order state, unique request identifier, or backend-controlled transaction strategy to make order creation safer.
78. Order Summary Widget
Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
const Text('Subtotal'),
Text('₹700'),
],
),
const SizedBox(height: 8),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
const Text('Delivery Fee'),
Text('₹40'),
],
),
const Divider(),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
const Text(
'Total',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
Text(
'₹740',
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
),
],
)
79. Profile Screen
The profile screen can contain:
- User name.
- Email address.
- Phone number.
- Saved addresses.
- Favorite restaurants.
- Favorite foods.
- Order history.
- Help and support.
- Logout.
80. Logout Functionality
await FirebaseAuth.instance.signOut();
if (!context.mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (_) => const LoginScreen(),
),
(route) => false,
);
81. Complete User Journey
Open Application
|
v
Splash Screen
|
v
Login / Register
|
v
Home
|
+----> Search Food
|
+----> Browse Categories
|
+----> Browse Restaurants
|
v
Food Details
|
v
Add To Cart
|
v
Cart
|
v
Checkout
|
v
Select Address
|
v
Select Payment
|
v
Place Order
|
v
Order Created
|
v
Order Tracking
|
v
Delivered
|
v
Order History
82. Complete Application Architecture
UI Layer
|
+-- Screens
+-- Widgets
|
v
State Management
|
+-- Cart State
+-- User State
+-- Order State
|
v
Service Layer
|
+-- Authentication Service
+-- Firestore Service
+-- Order Service
|
v
Firebase
|
+-- Authentication
+-- Firestore
+-- Storage
+-- Messaging
83. Error Scenarios to Handle
| Scenario | Expected Handling |
| No Internet | Show a useful network error message. |
| Login Failed | Show authentication error. |
| Empty Cart | Prevent checkout. |
| Food Unavailable | Ask the customer to remove or replace the item. |
| Firestore Error | Show retry option. |
| Invalid Address | Request corrected address. |
| Image Failed | Display placeholder image. |
| Order Creation Failed | Do not show a false success message. |
84. Performance Optimization
- Use const widgets whenever possible.
- Avoid unnecessary rebuilds.
- Use ListView.builder for long lists.
- Use GridView.builder for large food collections.
- Cache remote images.
- Paginate large Firestore collections.
- Keep business logic outside UI widgets.
- Dispose controllers when they are no longer needed.
- Avoid expensive operations inside build().
85. Common Mistakes
- Putting all application code inside main.dart.
- Keeping cart state only inside one screen.
- Using hardcoded food data throughout multiple widgets.
- Ignoring loading and error states.
- Not validating checkout information.
- Using insecure Firestore rules.
- Performing unnecessary database reads.
- Ignoring responsive layouts.
- Not handling unavailable food items.
- Creating duplicate orders when the user taps the button multiple times.
86. Best Practices
- Use reusable widgets.
- Keep models separate from UI.
- Keep Firebase operations inside service classes.
- Use state management for shared application state.
- Use meaningful variable and class names.
- Validate all user input.
- Handle asynchronous operations safely.
- Show appropriate loading indicators.
- Provide useful error messages.
- Secure Firestore data.
- Keep secrets and sensitive configuration out of public source code where appropriate.
- Test important business logic independently.
87. Testing the Application
The application should be tested across different parts of the user journey.
| Test | Expected Result |
| Registration | User account is created successfully. |
| Login | User enters the application. |
| Food Search | Matching food is displayed. |
| Add to Cart | Food appears in the cart. |
| Quantity Update | Cart amount changes correctly. |
| Remove Item | Item disappears from cart. |
| Checkout | Required information is validated. |
| Place Order | Order is saved successfully. |
| Order History | User sees their orders. |
| Logout | User returns to login screen. |
88. Debugging Techniques
Use Flutter debugging tools to identify UI and logic problems.
flutter doctor
flutter analyze
flutter test
flutter run
Use debugPrint() instead of excessive regular print statements for debugging larger applications.
debugPrint('Cart total: $total');
89. Deployment Preparation
Before deploying the application:
- Remove debug-only code.
- Test release builds.
- Verify Firebase configuration.
- Verify Firestore security rules.
- Optimize images.
- Check application permissions.
- Test authentication.
- Test checkout and order creation.
- Test different screen sizes.
- Review application performance.
90. Flutter Build Commands
Build an Android APK:
flutter build apk
Build an Android App Bundle:
flutter build appbundle
Build for web:
flutter build web
91. Final Food Delivery Project Checklist
- Flutter project created.
- Project architecture configured.
- Models created.
- Reusable widgets created.
- Home screen completed.
- Restaurant screen completed.
- Food detail screen completed.
- Search implemented.
- Cart implemented.
- Quantity management implemented.
- Checkout implemented.
- Authentication implemented.
- Firestore configured.
- Food data connected.
- Order data connected.
- Order history implemented.
- Order status implemented.
- Profile screen implemented.
- Responsive design tested.
- Error handling added.
- Security rules reviewed.
- Release build tested.
92. Real-World Features You Can Add
After completing the basic application, the project can be expanded with additional features.
- Live delivery tracking.
- Google Maps integration.
- Multiple saved addresses.
- Restaurant search.
- Advanced filtering.
- Coupon system.
- Discount management.
- Wishlist.
- Food ratings.
- Restaurant reviews.
- Push notifications.
- Online payment gateway.
- Order cancellation.
- Scheduled delivery.
- Restaurant admin panel.
- Delivery partner application.
93. Advanced Application Architecture
As the project grows, a clean architecture approach can separate presentation, domain, and data responsibilities.
Presentation
|
+-- Screens
+-- Widgets
+-- Controllers / Providers
|
v
Domain
|
+-- Entities
+-- Use Cases
+-- Repository Contracts
|
v
Data
|
+-- Models
+-- Repository Implementations
+-- Firebase Services
+-- Remote Data Sources
94. Why This Project Is Useful
This project demonstrates how Flutter concepts are combined in a real application instead of being used independently.
| Concept | Practical Usage |
| Widgets | Build the complete interface. |
| Navigation | Move between application screens. |
| State Management | Manage cart and user state. |
| Models | Represent food, restaurants, and orders. |
| Firebase Auth | Manage users. |
| Firestore | Store application data. |
| Streams | Display real-time updates. |
| Forms | Collect user information. |
| Responsive Design | Support different screen sizes. |
| Error Handling | Handle failures gracefully. |
95. Interview Questions Based on This Project
Question 1: Why should a food delivery application use state management?
State management allows shared data such as cart items, quantities, authentication state, and order information to be accessed and updated across multiple screens without tightly coupling those screens together.
Question 2: Why should models be created?
Models provide a structured representation of application data and make the code easier to understand, maintain, and transform between Firebase data and Flutter objects.
Question 3: Why use Firestore?
Firestore provides cloud-based document storage and supports real-time data synchronization, making it suitable for data such as restaurants, food items, users, and orders.
Question 4: How do you calculate the cart total?
The cart total can be calculated by multiplying each food item's price by its quantity and then adding the resulting values together.
Question 5: How can duplicate cart items be prevented?
Before adding a food item, check whether an item with the same unique food ID already exists in the cart. If it exists, increase its quantity instead of creating another cart entry.
Question 6: How can real-time order status be implemented?
Firestore document streams can listen for changes to the order document. When the restaurant or admin changes the order status, the customer interface can update automatically.
Question 7: Why should Firestore security rules be used?
Security rules control which authenticated users can read or write specific database resources. They help prevent unauthorized access to application data.
Question 8: What happens when an order is placed?
The application validates the cart and checkout information, calculates the final amount, creates an order record, clears the cart after successful creation, and navigates the user to an order confirmation or tracking screen.
Question 9: How can the application be made responsive?
Responsive layouts can use MediaQuery, LayoutBuilder, Expanded, Flexible, adaptive grids, and different layout structures based on available screen width.
Question 10: What improvements can be added to this project?
Advanced features can include online payments, maps, live delivery tracking, push notifications, coupons, ratings, reviews, scheduled orders, restaurant management, and delivery partner management.
96. Final Project Summary
The Flutter Food Delivery App is a complete practical project that demonstrates how to build a real-world mobile application using Flutter and Dart. The project starts with the fundamentals of Flutter UI and gradually introduces reusable components, navigation, models, cart state management, authentication, Firestore, order processing, real-time updates, responsive design, validation, error handling, and deployment preparation.
By completing this project, learners gain practical experience in connecting multiple Flutter concepts into one complete application workflow.
97. Skills You Learn From This Project
- Flutter application architecture.
- Dart object-oriented programming.
- Reusable widget development.
- Flutter navigation.
- Form handling and validation.
- State management.
- Cart implementation.
- Firebase Authentication.
- Cloud Firestore.
- Real-time application updates.
- CRUD operations.
- Responsive Flutter UI.
- Error and loading state management.
- Application performance optimization.
- Production-oriented Flutter development.
98. Learning Resources
For structured Flutter learning and additional practical training, explore the JustAcademy Flutter Training Course.
To register for a course demo, visit Register for Flutter Course Demo.
99. Final Project Flow
Flutter Food Delivery App
|
+-- Authentication
| |
| +-- Register
| +-- Login
| +-- Logout
|
+-- Home
| |
| +-- Search
| +-- Categories
| +-- Restaurants
| +-- Popular Food
|
+-- Food
| |
| +-- Food List
| +-- Food Details
| +-- Add To Cart
|
+-- Cart
| |
| +-- Quantity
| +-- Remove Item
| +-- Subtotal
| +-- Delivery Fee
| +-- Tax
| +-- Total
|
+-- Checkout
| |
| +-- Address
| +-- Payment
| +-- Place Order
|
+-- Orders
| |
| +-- Order History
| +-- Order Status
| +-- Real-Time Updates
|
+-- Profile
|
+-- User Information
+-- Addresses
+-- Favorites
+-- Logout
100. Conclusion
Building a Food Delivery App in Flutter is an excellent practical project for understanding complete application development. It combines UI design, reusable widgets, navigation, state management, Firebase Authentication, Firestore database operations, cart management, checkout processing, order tracking, responsive layouts, validation, error handling, and production preparation.
The same architecture can also be extended into larger applications such as restaurant management systems, grocery delivery applications, e-commerce platforms, cloud kitchens, meal subscription applications, and other marketplace-based applications.